FreeRTOS 任务的状态

xTaskCreate函数创建任务时,即为任务赋予了优先级。可以使用vTaskPrioritySet函数来修改任务的优先级,它的第一个参数是任务句柄、第二个参数是优先级,这个函数必须在调度器启动之后才能调用。相应的,uxTaskPriority函数返回一个任务的优先级。

1#include <task.h>
2void vTaskPrioritySet(TaskHandle_t xTask,UBaseType_t uxNewPriority);
3 
4UBaseType_t uxTaskPriorityGet(TaskHandle_t xTask);
5//返回任务的优先级

对于优先级相同的任务,FreeRTOS采用时间片轮询调度,每个任务轮流执行一段微小的时间。下面这段代码创建两个优先级相同的任务打印串口消息:

1#include <stm32f4xx.h>
2#include <FreeRTOS.h>
3#include <task.h>
4#include <uart.h>
5 
6void task1(void* args);
7void task2(void* args);
8 
9int main()
10{
11    //配置USART1
12    USART1_Config();
13    //创建任务
14    TaskHandle_t h1,h2;
15    xTaskCreate(task1,"task1",configMINIMAL_STACK_SIZE,"hello\n",1,&h1);
16    xTaskCreate(task2,"task2",configMINIMAL_STACK_SIZE,"world\n",1,&h2);
17     
18    //开启任务调度
19    vTaskStartScheduler();
20    while(1);
21}
22 
23 
24void task1(void* args)
25{
26    int i = 0;
27    while(1)
28    {
29        //循环5次后删除自己
30        if(i >= 5)
31        {
32            vTaskDelete(NULL);
33        }
34        //打印参数
35        USART_printf(USART1,args);
36        i++;
37    }
38}
39 
40void task2(void* args)
41{
42    int i = 0;
43    while(1)
44    {
45        //循环10次后删除自己
46        if(i >= 10)
47        {
48            vTaskDelete(NULL);
49        }
50        //打印参数
51        USART_printf(USART1,args);
52        i++;
53    }
54}

Image

对于优先级不同的任务,FreeRTOS采用抢占式调度,高优先级的任务优先执行。将上面代码中的task1优先级设为2,task1会优先执行:

1#include <stm32f4xx.h>
2#include <FreeRTOS.h>
3#include <task.h>
4#include <uart.h>
5 
6void task1(void* args);
7void task2(void* args);
8 
9int main()
10{
11    //配置USART1
12    USART1_Config();
13    //创建任务
14    TaskHandle_t h1,h2;
15    xTaskCreate(task1,"task1",configMINIMAL_STACK_SIZE,"hello\n",2,&h1);
16    xTaskCreate(task2,"task2",configMINIMAL_STACK_SIZE,"world\n",1,&h2);
17    //开启任务调度
18    vTaskStartScheduler();
19    while(1);
20}
21 
22 
23void task1(void* args)
24{
25    int i = 0;
26    while(1)
27    {
28        //循环5次后删除自己
29        if(i >= 5)
30        {
31            vTaskDelete(NULL);
32        }
33        //打印参数
34        USART_printf(USART1,args);
35        i++;
36    }
37}
38 
39void task2(void* args)
40{
41    int i = 0;
42    while(1)
43    {
44        //循环10次后删除自己
45        if(i >= 10)
46        {
47            vTaskDelete(NULL);
48        }
49        //打印参数
50        USART_printf(USART1,args);
51        i++;
52    }
53}

Image

FreeRTOS中任务存在4种状态:运行就绪阻塞挂起。调度器总是让运行和就绪状态的任务中,优先级最高的任务进入运行状态。

状态说明
运行状态(Running)正在运行的任务处于运行状态
就绪状态(Ready)可以运行但没有运行的任务处于就绪状态
阻塞状态(Blocked)等待某一事件而不能运行的任务处于阻塞状态
挂起状态(Suspended)调用vTaskSuspend挂起的任务处于挂起状态

Image

运行状态的任务可以通过调用阻塞函数进入阻塞状态,阻塞解除的事件可以让阻塞状态的任务进入就绪状态,vTaskSuspend函数让任务进入挂起状态,vTaskResume函数让挂起的任务进入就绪状态。

vTaskDelay是一个阻塞函数,它让任务进入阻塞状态等待一个定时事件,它的参数是定时事件的延时时间(周期数)。当定时时间到达,就会产生定时事件,让任务进入就绪状态。

1#include <task.h>
2void vTaskDelay(const TickType_t xTicksToDelay);

portTICK_RATE_MS表示每毫秒的周期数,用期望延时的毫秒数除以她可以得到相应的周期数,例如vTaskDelay(1000/portTICK_RATE_MS)延时1000毫秒。

仍然让task1的优先级高于task2,在task1中调用vTaskDelay阻塞,在task1阻塞期间,task2是优先级最高的任务,因此运行task2:

1#include <stm32f4xx.h>
2#include <FreeRTOS.h>
3#include <task.h>
4#include <uart.h>
5 
6void task1(void* args);
7void task2(void* args);
8 
9int main()
10{
11    //配置USART1
12    USART1_Config();
13    //创建任务
14    TaskHandle_t h1,h2;
15    xTaskCreate(task1,"task1",configMINIMAL_STACK_SIZE,"hello\n",2,&h1);
16    xTaskCreate(task2,"task2",configMINIMAL_STACK_SIZE,"world\n",1,&h2);
17    //开启任务调度
18    vTaskStartScheduler();
19    while(1);
20}
21 
22 
23void task1(void* args)
24{
25    int i = 0;
26    //阻塞1000ms
27    vTaskDelay(1000/portTICK_RATE_MS);
28    while(1)
29    {
30        //循环5次后删除自己
31        if(i >= 5)
32        {
33            vTaskDelete(NULL);
34        }
35        //打印参数
36        USART_printf(USART1,args);
37        i++;
38    }
39}
40 
41void task2(void* args)
42{
43    int i = 0;
44    while(1)
45    {
46        //循环10次后删除自己
47        if(i >= 10)
48        {
49            vTaskDelete(NULL);
50        }
51        //打印参数
52        USART_printf(USART1,args);
53        i++;
54    }
55}

Image

挂起状态的任务对于调度器而言是不可见的。vTaskSuspend让任务进入挂起状态,vTaskResume让挂起的任务进入就绪状态,它们的参数都是要操作任务的句柄。

1#include <task.h>
2void vTaskSuspend(TaskHandle_t xTaskToSuspend);
3void vTaskResume(TaskHandle_t xTaskToResume);